#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "pcb.h"
#include "sim.h"

#define FALSE 0
#define TRUE 1

/*global*/
unsigned clock;

/*
*The follow parts are the operations of pcb, 
*see comments in pcb.h
*/

/* creates the PCB struct */
PCB* createPCB(char* CommandName,unsigned StartTime,double cputime,unsigned IOCount,unsigned pid)
{
	PCB* pcb;
	
	pcb = (PCB*) malloc(sizeof(PCB));
	if(NULL == pcb){
		fprintf(stderr, "Failed to malloc PCB struct\n");
		exit(EXIT_FAILURE);
	}

	pcb->pid = pid;
	pcb->name = CommandName;
	pcb->start_time = StartTime;
	pcb->timeline_size = trunc ((IOCount + 8191) / 8192);
	pcb->cpu_time = 1000*cputime; /* to ms */
	pcb->completed_io_ops = 0;
	pcb->completed_cpu_time = 0;
	pcb->time_slice = 0;
	pcb->completed_time_slice = 0;
	pcb->priority = 0;
	pcb->io_timeline = makeTimeline(pcb->timeline_size, pcb->cpu_time);

	return pcb;
}

/* destroys the PCB struc */
void destroyPCB(PCB* pcb)
{
	free(pcb->name);
	if(pcb->timeline_size) {
		free(pcb->io_timeline);
	}
	free(pcb);
}
/* set the total cputime used by the process */
void setCpuTime(PCB* pcb, unsigned time)
{
	pcb->cpu_time=time;
}
/* returns the total cputime used by the process */
unsigned getCpuTime(PCB* pcb)
{
	return pcb->cpu_time;
}

/* sets the completed cpu time */
void setCompletedTime(PCB* pcb, unsigned time)
{
	pcb->completed_cpu_time=time;
}
/* returns the completed cpu time */
unsigned getCompletedTime(PCB* pcb)
{
	return pcb->completed_cpu_time;
}
/* sets the program's absolute start time */
void setStartTime(PCB* pcb, unsigned time)
{
	pcb->start_time=time;
}
/* returns the program's absolute start time */
unsigned getStartTime(PCB* pcb)
{
	return pcb->start_time;
}
/* sets the process's pid */
void setPid(PCB* pcb, unsigned pid)
{
	pcb->pid=pid;
}
/* return the process's pid */
unsigned getPid(PCB* pcb)
{
	return pcb->pid;
}
/* get the process's prioirty */
void setPriority(PCB* pcb, unsigned priority)
{
	pcb->priority=priority;
}
/* return the process's prioirty */
unsigned getPriority(PCB* pcb)
{
	return pcb->priority;
}
/* sets the completed slice time */
void setCompletedSlice(PCB* pcb, unsigned slice)
{
	pcb->completed_time_slice=slice;
}
/* set the number of completed io operations */
void setCompletedIO(PCB* pcb, unsigned value)
{
	pcb->completed_io_ops = value;
}
/* returns the assigned timeslice */
unsigned getCompletedSlice(PCB* pcb)
{
	return pcb->completed_time_slice;
}
/* sets the assigned timeslice */
void setTimeSlice(PCB* pcb, unsigned slice)
{
	pcb->time_slice=slice;
}
/* get the slice time */
unsigned getTimeSlice(PCB* pcb)
{
	return pcb->time_slice;
}
/* get the  time line size */
unsigned getTimeLineSize(PCB* pcb)
{
	return pcb->timeline_size;
}
/* returns the cpu time that an io is to occur !!after!! */
unsigned getTimeLineValue(PCB* pcb, unsigned index)
{
	return pcb->io_timeline[index];
}
/* returns the number of completed io operations */
unsigned getCompletedIO(PCB* pcb)
{
	return pcb->completed_io_ops;
}
/* retunrs the name of the process */
char* getName(PCB* pcb)
{
	return pcb->name;
}



/* creates an array of IO times relative to CPUTime */
unsigned* makeTimeline(unsigned IOOperations,unsigned CPUTime)
{
	int tmp_time = 0;
	int space, i, remainder;
	unsigned timeline_size = IOOperations;
	unsigned* io_timeline;

	/* no io ops == no timeline */
	if(IOOperations== 0 ){
		return NULL;
	}

	io_timeline = (unsigned*) malloc( sizeof(unsigned*) * timeline_size );
	if(NULL == io_timeline){
		fprintf(stderr, "Failed to malloc timeline\n");
		exit(EXIT_FAILURE);
	}

	remainder = CPUTime % IOOperations;
	space = CPUTime / IOOperations;
	if(0 == remainder || CPUTime>IOOperations){
		for(i=0;i<timeline_size;i++){
			tmp_time += space;
			io_timeline[i] = tmp_time;
		}
	} else {
		/* io interleaved with cpu, until just IO left */
		for(i=0;i<timeline_size;i++){
			if(tmp_time != CPUTime){
				tmp_time++;
			}
        	io_timeline[i] = tmp_time;	
		}
		
	}
	return  io_timeline;
}
/* returns 1 if process has completed all cpu and io operations */
int isProcessComplete(PCB* pcb)
{
	if(pcb->cpu_time == (pcb->completed_cpu_time)
    	&& pcb->completed_io_ops == pcb->timeline_size){
		return 1;
	}
	return 0;
}

/*test*/

void printTimeline(PCB *pcb)
{
	int i;
	printf("\tIO Timeline: ");
	for(i=0;i<pcb->timeline_size;i++){
		printf("%u ", pcb->io_timeline[i]);
	}
	printf("\n");
}

void printStartPCB(PCB *pcb)
{
	printPCBState("START PROCESS", pcb);
    printTimeline(pcb);
}

void printPCBState(const char* label, PCB* pcb)
{
	if(NULL != pcb ) printf("\t%s PROC:%s PID:%u CPUTime:%u/%u Slice:%u/%u IOOPS:%u/%u CLK:%u\n",
	    label,
		pcb->name, pcb->pid,
		pcb->completed_cpu_time, pcb->cpu_time,
    	pcb->completed_time_slice, pcb->time_slice,
		pcb->completed_io_ops, pcb->timeline_size,
		clock);
}
